package cn.zhf.shop.utils;


import cn.zhf.shop.entity.Message;
import cn.zhf.shop.entity.Orders;
import cn.zhf.shop.mapper.OrdersMapper;
import cn.zhf.shop.mapper.ShoppingOrderMapper;
import cn.zhf.shop.sdk.AcpService;
import cn.zhf.shop.sdk.DemoBase;
import cn.zhf.shop.sdk.LogUtil;
import cn.zhf.shop.sdk.SDKConfig;
import cn.zhf.shop.service.impl.OrdersServiceImpl;
import com.sun.net.httpserver.Authenticator;

import javax.annotation.Resource;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: 王泽
 * @Description:  银联工具类
 */

public class UnionPay {


    @Resource
    private static OrdersServiceImpl orderService;

    /**
     * 开通并直接直接消费,下方的商家号为测试号
     * @param txnAmt
     * @throws IOException
     */
    public static String pay(String txnAmt, String sms , String cardId, String orderId) throws IOException {

        String txnTime = DemoBase.getCurrentTime();

        Map<String, String> contentData = new HashMap<String, String>();
        /***银联全渠道系统，产品参数，除了encoding自行选择外其他不需修改***/

        contentData.put("version", DemoBase.version);                  //版本号

        contentData.put("encoding", DemoBase.encoding);           //字符集编码 可以使用UTF-8,GBK两种方式

        contentData.put("signMethod", SDKConfig.getConfig().getSignMethod()); //签名方法

        contentData.put("txnType", "01");                              //交易类型 01-消费

        contentData.put("txnSubType", "01");                           //交易子类型 01-消费

        contentData.put("bizType", "000301");                          //业务类型 认证支付2.0

        contentData.put("channelType", "07");                          //渠道类型07-PC

        /***商户接入参数***/

        contentData.put("merId", DemoBase.merId);                                         //商户号码（本商户号码仅做为测试调通交易使用，该商户号配置了需要对敏感信息加密）测试时请改成自己申请的商户号，【自己注册的测试777开头的商户号不支持代收产品】

        contentData.put("accessType", "0");                            //接入类型，商户接入固定填0，不需修改

        contentData.put("orderId", orderId);                                //商户订单号，8-40位数字字母，不能含“-”或“_”，可以自行定制规则

        contentData.put("txnTime", txnTime);                                        //订单发送时间，格式为YYYYMMDDhhmmss，必须取当前时间，否则会报txnTime无效

        contentData.put("currencyCode", "156");                                         //交易币种（境内商户一般是156 人民币）

        contentData.put("txnAmt", txnAmt);                                                 //交易金额，单位分，不要带小数点

        contentData.put("accType", "01");                              //账号类型


        Map<String,String> customerInfoMap = new HashMap<String,String>();

        customerInfoMap.put("smsCode", sms);                           //短信验证码,测试环境不会真实收到短信，固定填111111

        String accNo = AcpService.encryptData(cardId, DemoBase.encoding);  //这里测试的时候使用的是测试卡号，正式环境请使用真实卡号

        contentData.put("accNo", accNo);

        contentData.put("encryptCertId",AcpService.getEncryptCertId());       //加密证书的certId，配置在acp_sdk.properties文件 acpsdk.encryptCert.path属性下

        String customerInfoStr = AcpService.getCustomerInfoWithEncrypt(customerInfoMap,cardId,DemoBase.encoding);
        contentData.put("customerInfo", customerInfoStr);
        contentData.put("backUrl", DemoBase.backUrl);

        /**对请求参数进行签名并发送http post请求，接收同步应答报文**/

        Map<String, String> reqData = AcpService.sign(contentData,DemoBase.encoding);                 //报文中certId,signature的值是在signData方法中获取并自动赋值的，只要证书配置正确即可。

        String requestBackUrl = SDKConfig.getConfig().getBackRequestUrl();                    //交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrl

        Map<String, String> rspData = AcpService.post(reqData,requestBackUrl,DemoBase.encoding); //发送请求报文并接受同步应答（默认连接超时时间30秒，读取返回结果超时时间30秒）;这里调用signData之后，调用submitUrl之前不能对submitFromData中的键值对做任何修改，如果修改会导致验签不通过

        /**对应答码的处理，请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**/

        StringBuffer parseStr = new StringBuffer("");

        if(!rspData.isEmpty()){

            if(AcpService.validate(rspData, DemoBase.encoding)){

            //    LogUtil.writeLog("验证签名成功");

                String respCode = rspData.get("respCode") ;

                if(("00").equals(respCode)){


                    return respCode;

                }else if(("03").equals(respCode)||
                        ("04").equals(respCode)||
                        ("05").equals(respCode)){
                    //后续需发起交易状态查询交易确定交易状态
                    //TODO
                    return query(orderId,txnTime);
                }else{
                    //其他应答码为失败请排查原因
                    //TODO
                    return respCode;
                }
            }else{
                LogUtil.writeErrorLog("验证签名失败");
                //TODO 检查验证签名失败的原因
                return rspData.get("respCode");
            }
        }else{
            //未返回正确的http状态
            LogUtil.writeErrorLog("未获取到返回报文或返回http状态码非200");
            return rspData.get("respCode");
        }

    }

    /**
     * 申请消费验证码
     * @param phoneNo
     * @param txnAmt
     */
    public static Message<?> SMS(String phoneNo,String txnAmt,String orderId){

        String txnTime = DemoBase.getCurrentTime();

        Map<String, String> contentData = new HashMap<String, String>();

        /***银联全渠道系统，产品参数，除了encoding自行选择外其他不需修改***/
        contentData.put("version", DemoBase.version);                  //版本号
        contentData.put("encoding", DemoBase.encoding);                //字符集编码 可以使用UTF-8,GBK两种方式
        contentData.put("signMethod", SDKConfig.getConfig().getSignMethod()); //签名方法
        contentData.put("txnType", "77");                              //交易类型 11-代收
        contentData.put("txnSubType", "02");                           //交易子类型 02-消费短信
        contentData.put("bizType", "000301");                          //业务类型 认证支付2.0
        contentData.put("channelType", "07");                          //渠道类型07-PC

        /***商户接入参数***/
        contentData.put("merId", DemoBase.merId);                   			   //商户号码（本商户号码仅做为测试调通交易使用，该商户号配置了需要对敏感信息加密）测试时请改成自己申请的商户号，【自己注册的测试777开头的商户号不支持代收产品】
        contentData.put("accessType", "0");                            //接入类型，商户接入固定填0，不需修改
        contentData.put("orderId", orderId);             			   //商户订单号，8-40位数字字母，不能含“-”或“_”，可以自行定制规则
        contentData.put("txnTime", txnTime);         				   //订单发送时间，格式为YYYYMMDDhhmmss，必须取当前时间，否则会报txnTime无效
        contentData.put("currencyCode", "156");						   //交易币种（境内商户一般是156 人民币）
        contentData.put("txnAmt", txnAmt);							   //交易金额，单位分，不要带小数点
        contentData.put("accType", "01");                              //账号类型

        //必送手机号
        Map<String,String> customerInfoMap = new HashMap<String,String>();
        customerInfoMap.put("phoneNo", phoneNo);

        ////////////如果商户号开通了【商户对敏感信息加密】的权限那么需要对 accNo,phoneNo加密使用：
        String accNo = AcpService.encryptData("6216261000000000018", "UTF-8");  //这里测试的时候使用的是测试卡号，正式环境请使用真实卡号
        contentData.put("accNo", accNo);
        contentData.put("encryptCertId",AcpService.getEncryptCertId());       //加密证书的certId，配置在acp_sdk.properties文件 acpsdk.encryptCert.path属性下
        String customerInfoStr = AcpService.getCustomerInfoWithEncrypt(customerInfoMap,null,DemoBase.encoding);


        contentData.put("customerInfo", customerInfoStr);
        //contentData.put("reqReserved", "透传字段");        					//请求方保留域，透传字段（可以实现商户自定义参数的追踪）本交易的后台通知,对本交易的交易状态查询交易、对账文件中均会原样返回，商户可以按需上传，长度为1-1024个字节。出现&={}[]符号时可能导致查询接口应答报文解析失败，建议尽量只传字母数字并使用|分割，或者可以最外层做一次base64编码(base64编码之后出现的等号不会导致解析失败可以不用管)。

        /**对请求参数进行签名并发送http post请求，接收同步应答报文**/
        Map<String, String> reqData = AcpService.sign(contentData,DemoBase.encoding);			 //报文中certId,signature的值是在signData方法中获取并自动赋值的，只要证书配置正确即可。
        String requestBackUrl = SDKConfig.getConfig().getBackRequestUrl();   								 //交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrl
        Map<String, String> rspData = AcpService.post(reqData,requestBackUrl,DemoBase.encoding); //发送请求报文并接受同步应答（默认连接超时时间30秒，读取返回结果超时时间30秒）;这里调用signData之后，调用submitUrl之前不能对submitFromData中的键值对做任何修改，如果修改会导致验签不通过



        /**对应答码的处理，请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**/
        //应答码规范参考open.unionpay.com帮助中心 下载  产品接口规范  《平台接入接口规范-第5部分-附录》

        if(!rspData.isEmpty()){
            if(AcpService.validate(rspData, DemoBase.encoding)){
          //      LogUtil.writeLog("验证签名成功");
                String respCode = rspData.get("respCode") ;
                if(("00").equals(respCode)){
                    //成功
                    //TODO
                    return Message.success("验证码发送成功");
                }else{
                    //其他应答码为失败请排查原因或做失败处理
                    //TODO
                    String rspMessage = DemoBase.genHtmlResult(rspData);
                    return Message.fail("验证码发送失败").add(rspMessage);
                }
            }else{
                LogUtil.writeErrorLog("验证签名失败");
                //TODO 检查验证签名失败的原因
                String rspMessage = DemoBase.genHtmlResult(rspData);
                return Message.fail("验证码发送失败").add(rspMessage);
            }
        }else{
            //未返回正确的http状态
            LogUtil.writeErrorLog("未获取到返回报文或返回http状态码非200");
            String rspMessage = DemoBase.genHtmlResult(rspData);
            return Message.fail("验证码发送失败").add(rspMessage);
        }


    }

    /**
     * 根据订单号查询,订单状态
     */
    public static String query(String orderId, String txnTime){

        Map<String, String> data = new HashMap<String, String>();

        /***银联全渠道系统，产品参数，除了encoding自行选择外其他不需修改***/
        data.put("version", DemoBase.version);                 //版本号
        data.put("encoding", DemoBase.encoding);               //字符集编码 可以使用UTF-8,GBK两种方式
        data.put("signMethod", SDKConfig.getConfig().getSignMethod()); //签名方法
        data.put("txnType", "00");                             //交易类型 00-默认
        data.put("txnSubType", "00");                          //交易子类型  默认00
        data.put("bizType", "000301");                         //业务类型

        /***商户接入参数***/
        data.put("merId", DemoBase.merId);                  			   //商户号码，请改成自己申请的商户号或者open上注册得来的777商户号测试
        data.put("accessType", "0");                           //接入类型，商户接入固定填0，不需修改

        /***要调通交易以下字段必须修改***/
        data.put("orderId", orderId);                 //****商户订单号，每次发交易测试需修改为被查询的交易的订单号
        data.put("txnTime", txnTime);                 //****订单发送时间，每次发交易测试需修改为被查询的交易的订单发送时间

        /**请求参数设置完毕，以下对请求参数进行签名并发送http post请求，接收同步应答报文------------->**/

        Map<String, String> reqData = AcpService.sign(data,DemoBase.encoding);			//报文中certId,signature的值是在signData方法中获取并自动赋值的，只要证书配置正确即可。
        String url = SDKConfig.getConfig().getSingleQueryUrl();								//交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.singleQueryUrl
        Map<String, String> rspData = AcpService.post(reqData, url,DemoBase.encoding); //发送请求报文并接受同步应答（默认连接超时时间30秒，读取返回结果超时时间30秒）;这里调用signData之后，调用submitUrl之前不能对submitFromData中的键值对做任何修改，如果修改会导致验签不通过

        /**对应答码的处理，请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**/
        //应答码规范参考open.unionpay.com帮助中心 下载  产品接口规范  《平台接入接口规范-第5部分-附录》
        if(!rspData.isEmpty()){
            if(AcpService.validate(rspData, DemoBase.encoding)){
                LogUtil.writeLog("验证签名成功");
                if(("00").equals(rspData.get("respCode"))){//如果查询交易成功
                    String origRespCode = rspData.get("origRespCode");
                    if(("00").equals(origRespCode)){
                        //交易成功，更新商户订单状态
                        //TODO

                        return origRespCode;
                    }else if(("03").equals(origRespCode)||
                            ("04").equals(origRespCode)||
                            ("05").equals(origRespCode)){
                        //订单处理中或交易状态未明，需稍后发起交易状态查询交易 【如果最终尚未确定交易是否成功请以对账文件为准】
                        //TODO
                        return query(orderId,txnTime);
                    }else{
                        //其他应答码为交易失败
                        //TODO
                        return origRespCode;
                    }
                }else if(("34").equals(rspData.get("respCode"))){
                    //订单不存在，可认为交易状态未明，需要稍后发起交易状态查询，或依据对账结果为准
                    return rspData.get("respCode");

                }else{//查询交易本身失败，如应答码10/11检查查询报文是否正确
                    //TODO
                    return rspData.get("respCode");
                }
            }else{
                LogUtil.writeErrorLog("验证签名失败");
                //TODO 检查验证签名失败的原因
                return rspData.get("respCode");
            }
        }else{
            //未返回正确的http状态
            LogUtil.writeErrorLog("未获取到返回报文或返回http状态码非200");
            return rspData.get("respCode");
        }

    }

    /**
     * 退款
     */
    public static void refund(String origQryId,String txnAmt){
        Map<String, String> data = new HashMap<String, String>();

        /***银联全渠道系统，产品参数，除了encoding自行选择外其他不需修改***/
        data.put("version", DemoBase.version);               //版本号
        data.put("encoding", DemoBase.encoding);             //字符集编码 可以使用UTF-8,GBK两种方式
        data.put("signMethod", SDKConfig.getConfig().getSignMethod()); //签名方法
        data.put("txnType", "04");                           //交易类型 04-退货
        data.put("txnSubType", "00");                        //交易子类型  默认00
        data.put("bizType", "000301");                       //业务类型
        data.put("channelType", "08");                       //渠道类型，07-PC，08-手机

        /***商户接入参数***/
        data.put("merId", DemoBase.merId);                //商户号码，请改成自己申请的商户号或者open上注册得来的777商户号测试
        data.put("accessType", "0");                         //接入类型，商户接入固定填0，不需修改
        data.put("orderId", DemoBase.getOrderId());          //商户订单号，8-40位数字字母，不能含“-”或“_”，可以自行定制规则，重新产生，不同于原消费
        data.put("txnTime", DemoBase.getCurrentTime());      //订单发送时间，格式为YYYYMMDDhhmmss，必须取当前时间，否则会报txnTime无效
        data.put("currencyCode", "156");                     //交易币种（境内商户一般是156 人民币）
        data.put("txnAmt", txnAmt);                          //****退货金额，单位分，不要带小数点。退货金额小于等于原消费金额，当小于的时候可以多次退货至退货累计金额等于原消费金额
        data.put("backUrl", DemoBase.backUrl);               //后台通知地址，后台通知参数详见open.unionpay.com帮助中心 下载  产品接口规范  网关支付产品接口规范 退货交易 商户通知,其他说明同消费交易的后台通知

        /***要调通交易以下字段必须修改***/
        data.put("origQryId", origQryId);      //****原消费交易返回的的queryId，可以从消费交易后台通知接口中或者交易状态查询接口中获取


        /**请求参数设置完毕，以下对请求参数进行签名并发送http post请求，接收同步应答报文------------->**/
        Map<String, String> reqData  = AcpService.sign(data,DemoBase.encoding);		//报文中certId,signature的值是在signData方法中获取并自动赋值的，只要证书配置正确即可。
        String url = SDKConfig.getConfig().getBackRequestUrl();									//交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrl
        Map<String, String> rspData = AcpService.post(reqData, url,DemoBase.encoding);//这里调用signData之后，调用submitUrl之前不能对submitFromData中的键值对做任何修改，如果修改会导致验签不通过

        /**对应答码的处理，请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**/
        //应答码规范参考open.unionpay.com帮助中心 下载  产品接口规范  《平台接入接口规范-第5部分-附录》
        if(!rspData.isEmpty()){
            if(AcpService.validate(rspData, DemoBase.encoding)){
        //        LogUtil.writeLog("验证签名成功");
                String respCode = rspData.get("respCode") ;
                if(("00").equals(respCode)){
                    //交易已受理(不代表交易已成功），等待接收后台通知更新订单状态,也可以主动发起 查询交易确定交易状态。
                    //TODO
                }else if(("03").equals(respCode)||
                        ("04").equals(respCode)||
                        ("05").equals(respCode)){
                    //后续需发起交易状态查询交易确定交易状态
                    //TODO
                }else{
                    //其他应答码为失败请排查原因
                    //TODO
                }
            }else{
                LogUtil.writeErrorLog("验证签名失败");
                //TODO 检查验证签名失败的原因
            }
        }else{
            //未返回正确的http状态
            LogUtil.writeErrorLog("未获取到返回报文或返回http状态码非200");
        }
        /**
         * 检查rspMessage的返回值是否是00,如果是证明发送欧冠短信成功,否则失败
         */
        String rspMessage = DemoBase.genHtmlResult(rspData);

    }


    /**
     * 分期付款
     * @param txnAmt
     * @param sms
     * @param cardId
     * @param number
     */

    public static String payByStages(String txnAmt, String sms,String cardId,String number,String orderId){

        Map<String, String> contentData = new HashMap<String, String>();
        /***银联全渠道系统，产品参数，除了encoding自行选择外其他不需修改***/

        String txnTime = DemoBase.getCurrentTime();

        contentData.put("version", DemoBase.version);                  //版本号

        contentData.put("encoding", DemoBase.encoding);           //字符集编码 可以使用UTF-8,GBK两种方式

        contentData.put("signMethod", SDKConfig.getConfig().getSignMethod()); //签名方法

        contentData.put("txnType", "01");                              //交易类型 01-消费

        contentData.put("txnSubType", "03");                           //交易子类型 03-分期付款

        contentData.put("bizType", "000301");                          //业务类型 认证支付2.0

        contentData.put("channelType", "08");                          //渠道类型07-PC

        contentData.put("instalTransInfo","{numberOfInstallments="+number+"}");//分期付款信息域，numberOfInstallments期数


        /***商户接入参数***/

        contentData.put("merId", DemoBase.merId);                                         //商户号码（本商户号码仅做为测试调通交易使用，该商户号配置了需要对敏感信息加密）测试时请改成自己申请的商户号，【自己注册的测试777开头的商户号不支持代收产品】

        contentData.put("accessType", "0");                            //接入类型，商户接入固定填0，不需修改

        contentData.put("orderId", orderId);                                //商户订单号，8-40位数字字母，不能含“-”或“_”，可以自行定制规则

        contentData.put("txnTime", txnTime);                                        //订单发送时间，格式为YYYYMMDDhhmmss，必须取当前时间，否则会报txnTime无效

        contentData.put("currencyCode", "156");                                         //交易币种（境内商户一般是156 人民币）

        contentData.put("txnAmt", txnAmt);                                                 //交易金额，单位分，不要带小数点

        contentData.put("accType", "01");                              //账号类型



        //消费：交易要素卡号+验证码看业务配置(默认要短信验证码)。

        Map<String,String> customerInfoMap = new HashMap<String,String>();

        customerInfoMap.put("smsCode", sms);                           //短信验证码,测试环境不会真实收到短信，固定填111111


        ////////////如果商户号开通了【商户对敏感信息加密】的权限那么需要对 accNo，pin和phoneNo，cvn2，expired加密（如果这些上送的话），对敏感信息加密使用：

        String accNo = AcpService.encryptData(cardId, DemoBase.encoding);  //这里测试的时候使用的是测试卡号，正式环境请使用真实卡号

        contentData.put("accNo", accNo);

        contentData.put("encryptCertId",AcpService.getEncryptCertId());       //加密证书的certId，配置在acp_sdk.properties文件 acpsdk.encryptCert.path属性下

        String customerInfoStr = AcpService.getCustomerInfoWithEncrypt(customerInfoMap,cardId,DemoBase.encoding);

        contentData.put("customerInfo", customerInfoStr);

        contentData.put("backUrl", DemoBase.backUrl);


        /**对请求参数进行签名并发送http post请求，接收同步应答报文**/

        Map<String, String> reqData = AcpService.sign(contentData,DemoBase.encoding);                 //报文中certId,signature的值是在signData方法中获取并自动赋值的，只要证书配置正确即可。

        String requestBackUrl = SDKConfig.getConfig().getBackRequestUrl();                    //交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrl

        Map<String, String> rspData = AcpService.post(reqData,requestBackUrl,DemoBase.encoding); //发送请求报文并接受同步应答（默认连接超时时间30秒，读取返回结果超时时间30秒）;这里调用signData之后，调用submitUrl之前不能对submitFromData中的键值对做任何修改，如果修改会导致验签不通过



        /**对应答码的处理，请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**/

        //应答码规范参考open.unionpay.com帮助中心 下载  产品接口规范  《平台接入接口规范-第5部分-附录》

        StringBuffer parseStr = new StringBuffer("");

        if(!rspData.isEmpty()){

            if(AcpService.validate(rspData, DemoBase.encoding)){

            //    LogUtil.writeLog("验证签名成功");

                String respCode = rspData.get("respCode") ;

                if(("00").equals(respCode)){
                    //交易已受理(不代表交易已成功），等待接收后台通知更新订单状态,也可以主动发起 查询交易确定交易状态。

                    //TODO


                    return respCode;

                }else if(("03").equals(respCode)||

                        ("04").equals(respCode)||

                        ("05").equals(respCode)){

                    //后续需发起交易状态查询交易确定交易状态
                    return query(orderId,txnTime);

                    //TODO

                }else{

                    //其他应答码为失败请排查原因

                    //TODO
                    return respCode;

                }

            }else{
                LogUtil.writeErrorLog("验证签名失败");

                //TODO 检查验证签名失败的原因
                return rspData.get("respCode");

            }
        }else{
            //未返回正确的http状态
            LogUtil.writeErrorLog("未获取到返回报文或返回http状态码非200");
            return rspData.get("respCode");
        }
    }



    /**
     * 开通并直接直接消费,下方的商家号为测试号-购物车的
     * @param txnAmt
     * @throws IOException
     */
    public static String payShop(String txnAmt, String sms , String cardId, String orderId) throws IOException {

        String txnTime = DemoBase.getCurrentTime();

        Map<String, String> contentData = new HashMap<String, String>();
        /***银联全渠道系统，产品参数，除了encoding自行选择外其他不需修改***/

        contentData.put("version", DemoBase.version);                  //版本号

        contentData.put("encoding", DemoBase.encoding);           //字符集编码 可以使用UTF-8,GBK两种方式

        contentData.put("signMethod", SDKConfig.getConfig().getSignMethod()); //签名方法

        contentData.put("txnType", "01");                              //交易类型 01-消费

        contentData.put("txnSubType", "01");                           //交易子类型 01-消费

        contentData.put("bizType", "000301");                          //业务类型 认证支付2.0

        contentData.put("channelType", "07");                          //渠道类型07-PC

        /***商户接入参数***/

        contentData.put("merId", DemoBase.merId);                                         //商户号码（本商户号码仅做为测试调通交易使用，该商户号配置了需要对敏感信息加密）测试时请改成自己申请的商户号，【自己注册的测试777开头的商户号不支持代收产品】

        contentData.put("accessType", "0");                            //接入类型，商户接入固定填0，不需修改

        contentData.put("orderId", orderId);                                //商户订单号，8-40位数字字母，不能含“-”或“_”，可以自行定制规则

        contentData.put("txnTime", txnTime);                                        //订单发送时间，格式为YYYYMMDDhhmmss，必须取当前时间，否则会报txnTime无效

        contentData.put("currencyCode", "156");                                         //交易币种（境内商户一般是156 人民币）

        contentData.put("txnAmt", txnAmt);                                                 //交易金额，单位分，不要带小数点

        contentData.put("accType", "01");                              //账号类型


        Map<String,String> customerInfoMap = new HashMap<String,String>();

        customerInfoMap.put("smsCode", sms);                           //短信验证码,测试环境不会真实收到短信，固定填111111

        String accNo = AcpService.encryptData(cardId, DemoBase.encoding);  //这里测试的时候使用的是测试卡号，正式环境请使用真实卡号

        contentData.put("accNo", accNo);

        contentData.put("encryptCertId",AcpService.getEncryptCertId());       //加密证书的certId，配置在acp_sdk.properties文件 acpsdk.encryptCert.path属性下

        String customerInfoStr = AcpService.getCustomerInfoWithEncrypt(customerInfoMap,cardId,DemoBase.encoding);
        contentData.put("customerInfo", customerInfoStr);
        contentData.put("backUrl", DemoBase.backUrl);

        /**对请求参数进行签名并发送http post请求，接收同步应答报文**/

        Map<String, String> reqData = AcpService.sign(contentData,DemoBase.encoding);                 //报文中certId,signature的值是在signData方法中获取并自动赋值的，只要证书配置正确即可。

        String requestBackUrl = SDKConfig.getConfig().getBackRequestUrl();                    //交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrl

        Map<String, String> rspData = AcpService.post(reqData,requestBackUrl,DemoBase.encoding); //发送请求报文并接受同步应答（默认连接超时时间30秒，读取返回结果超时时间30秒）;这里调用signData之后，调用submitUrl之前不能对submitFromData中的键值对做任何修改，如果修改会导致验签不通过

        /**对应答码的处理，请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**/

        StringBuffer parseStr = new StringBuffer("");

        if(!rspData.isEmpty()){

            if(AcpService.validate(rspData, DemoBase.encoding)){

       //         LogUtil.writeLog("验证签名成功");

                String respCode = rspData.get("respCode") ;

                if(("00").equals(respCode)){

                    return respCode;

                }else if(("03").equals(respCode)||
                        ("04").equals(respCode)||
                        ("05").equals(respCode)){
                    //后续需发起交易状态查询交易确定交易状态
                    //TODO
                    return queryshop(orderId,txnTime);
                }else{
                    //其他应答码为失败请排查原因
                    //TODO
                    return respCode;
                }
            }else{
                LogUtil.writeErrorLog("验证签名失败");
                //TODO 检查验证签名失败的原因
                return rspData.get("respCode");
            }
        }else{
            //未返回正确的http状态
            LogUtil.writeErrorLog("未获取到返回报文或返回http状态码非200");
            return rspData.get("respCode");
        }

    }


    /**
     * 分期付款-购物车
     * @param txnAmt
     * @param sms
     * @param cardId
     * @param number
     */

    public static String payByStagesShop(String txnAmt, String sms,String cardId,String number,String orderId){
        Map<String, String> contentData = new HashMap<String, String>();
        /***银联全渠道系统，产品参数，除了encoding自行选择外其他不需修改***/

        String txnTime = DemoBase.getCurrentTime();

        contentData.put("version", DemoBase.version);                  //版本号

        contentData.put("encoding", DemoBase.encoding);           //字符集编码 可以使用UTF-8,GBK两种方式

        contentData.put("signMethod", SDKConfig.getConfig().getSignMethod()); //签名方法

        contentData.put("txnType", "01");                              //交易类型 01-消费

        contentData.put("txnSubType", "03");                           //交易子类型 03-分期付款

        contentData.put("bizType", "000301");                          //业务类型 认证支付2.0

        contentData.put("channelType", "08");                          //渠道类型07-PC

        contentData.put("instalTransInfo","{numberOfInstallments="+number+"}");//分期付款信息域，numberOfInstallments期数


        /***商户接入参数***/

        contentData.put("merId", DemoBase.merId);                                         //商户号码（本商户号码仅做为测试调通交易使用，该商户号配置了需要对敏感信息加密）测试时请改成自己申请的商户号，【自己注册的测试777开头的商户号不支持代收产品】

        contentData.put("accessType", "0");                            //接入类型，商户接入固定填0，不需修改

        contentData.put("orderId", orderId);                                //商户订单号，8-40位数字字母，不能含“-”或“_”，可以自行定制规则

        contentData.put("txnTime", txnTime);                                        //订单发送时间，格式为YYYYMMDDhhmmss，必须取当前时间，否则会报txnTime无效

        contentData.put("currencyCode", "156");                                         //交易币种（境内商户一般是156 人民币）

        contentData.put("txnAmt", txnAmt);                                                 //交易金额，单位分，不要带小数点

        contentData.put("accType", "01");                              //账号类型



        //消费：交易要素卡号+验证码看业务配置(默认要短信验证码)。

        Map<String,String> customerInfoMap = new HashMap<String,String>();

        customerInfoMap.put("smsCode", sms);                           //短信验证码,测试环境不会真实收到短信，固定填111111


        ////////////如果商户号开通了【商户对敏感信息加密】的权限那么需要对 accNo，pin和phoneNo，cvn2，expired加密（如果这些上送的话），对敏感信息加密使用：

        String accNo = AcpService.encryptData(cardId, DemoBase.encoding);  //这里测试的时候使用的是测试卡号，正式环境请使用真实卡号

        contentData.put("accNo", accNo);

        contentData.put("encryptCertId",AcpService.getEncryptCertId());       //加密证书的certId，配置在acp_sdk.properties文件 acpsdk.encryptCert.path属性下

        String customerInfoStr = AcpService.getCustomerInfoWithEncrypt(customerInfoMap,cardId,DemoBase.encoding);

        contentData.put("customerInfo", customerInfoStr);

        contentData.put("backUrl", DemoBase.backUrl);


        /**对请求参数进行签名并发送http post请求，接收同步应答报文**/

        Map<String, String> reqData = AcpService.sign(contentData,DemoBase.encoding);                 //报文中certId,signature的值是在signData方法中获取并自动赋值的，只要证书配置正确即可。

        String requestBackUrl = SDKConfig.getConfig().getBackRequestUrl();                    //交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrl

        Map<String, String> rspData = AcpService.post(reqData,requestBackUrl,DemoBase.encoding); //发送请求报文并接受同步应答（默认连接超时时间30秒，读取返回结果超时时间30秒）;这里调用signData之后，调用submitUrl之前不能对submitFromData中的键值对做任何修改，如果修改会导致验签不通过



        /**对应答码的处理，请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**/

        //应答码规范参考open.unionpay.com帮助中心 下载  产品接口规范  《平台接入接口规范-第5部分-附录》

        StringBuffer parseStr = new StringBuffer("");

        if(!rspData.isEmpty()){

            if(AcpService.validate(rspData, DemoBase.encoding)){

                LogUtil.writeLog("验证签名成功");

                String respCode = rspData.get("respCode") ;

                if(("00").equals(respCode)){
                    //交易已受理(不代表交易已成功），等待接收后台通知更新订单状态,也可以主动发起 查询交易确定交易状态。

                    //TODO
                    return respCode;

                }else if(("03").equals(respCode)||

                        ("04").equals(respCode)||

                        ("05").equals(respCode)){

                    //后续需发起交易状态查询交易确定交易状态
                    return queryshop(orderId,txnTime);

                    //TODO

                }else{

                    //其他应答码为失败请排查原因

                    //TODO
                    return respCode;

                }

            }else{
                LogUtil.writeErrorLog("验证签名失败");

                //TODO 检查验证签名失败的原因
                return rspData.get("respCode");

            }
        }else{
            //未返回正确的http状态
            LogUtil.writeErrorLog("未获取到返回报文或返回http状态码非200");
            return rspData.get("respCode");
        }
    }


    /**
     * 根据订单号查询,订单状态
     */
    public static String queryshop(String orderId, String txnTime){

        Map<String, String> data = new HashMap<String, String>();

        /***银联全渠道系统，产品参数，除了encoding自行选择外其他不需修改***/
        data.put("version", DemoBase.version);                 //版本号
        data.put("encoding", DemoBase.encoding);               //字符集编码 可以使用UTF-8,GBK两种方式
        data.put("signMethod", SDKConfig.getConfig().getSignMethod()); //签名方法
        data.put("txnType", "00");                             //交易类型 00-默认
        data.put("txnSubType", "00");                          //交易子类型  默认00
        data.put("bizType", "000301");                         //业务类型

        /***商户接入参数***/
        data.put("merId", DemoBase.merId);                  			   //商户号码，请改成自己申请的商户号或者open上注册得来的777商户号测试
        data.put("accessType", "0");                           //接入类型，商户接入固定填0，不需修改

        /***要调通交易以下字段必须修改***/
        data.put("orderId", orderId);                 //****商户订单号，每次发交易测试需修改为被查询的交易的订单号
        data.put("txnTime", txnTime);                 //****订单发送时间，每次发交易测试需修改为被查询的交易的订单发送时间

        /**请求参数设置完毕，以下对请求参数进行签名并发送http post请求，接收同步应答报文------------->**/

        Map<String, String> reqData = AcpService.sign(data,DemoBase.encoding);			//报文中certId,signature的值是在signData方法中获取并自动赋值的，只要证书配置正确即可。
        String url = SDKConfig.getConfig().getSingleQueryUrl();								//交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.singleQueryUrl
        Map<String, String> rspData = AcpService.post(reqData, url,DemoBase.encoding); //发送请求报文并接受同步应答（默认连接超时时间30秒，读取返回结果超时时间30秒）;这里调用signData之后，调用submitUrl之前不能对submitFromData中的键值对做任何修改，如果修改会导致验签不通过

        /**对应答码的处理，请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**/
        //应答码规范参考open.unionpay.com帮助中心 下载  产品接口规范  《平台接入接口规范-第5部分-附录》
        if(!rspData.isEmpty()){
            if(AcpService.validate(rspData, DemoBase.encoding)){
                LogUtil.writeLog("验证签名成功");
                if(("00").equals(rspData.get("respCode"))){//如果查询交易成功
                    String origRespCode = rspData.get("origRespCode");
                    if(("00").equals(origRespCode)){
                        //交易成功，更新商户订单状态
                        //TODO

                        return origRespCode;
                    }else if(("03").equals(origRespCode)||
                            ("04").equals(origRespCode)||
                            ("05").equals(origRespCode)){
                        //订单处理中或交易状态未明，需稍后发起交易状态查询交易 【如果最终尚未确定交易是否成功请以对账文件为准】
                        //TODO
                        return queryshop(orderId,txnTime);
                    }else{
                        //其他应答码为交易失败
                        //TODO
                        return origRespCode;
                    }
                }else if(("34").equals(rspData.get("respCode"))){
                    //订单不存在，可认为交易状态未明，需要稍后发起交易状态查询，或依据对账结果为准
                    return rspData.get("origRespCode");

                }else{//查询交易本身失败，如应答码10/11检查查询报文是否正确
                    //TODO
                    return rspData.get("origRespCode");
                }
            }else{
                LogUtil.writeErrorLog("验证签名失败");
                //TODO 检查验证签名失败的原因
                return rspData.get("origRespCode");
            }
        }else{
            //未返回正确的http状态
            LogUtil.writeErrorLog("未获取到返回报文或返回http状态码非200");
            return rspData.get("origRespCode");
        }

    }






}




